#!/usr/bin/python3
#-*-coding:utf-8-*-

###############################################################
## Name       : auto_dff.py
## Author     : gaojiaming
## Date       : Mon Sep 25 16:44:59 CST 2023
## Description:
##
##
###############################################################

import sys
import os
import re
import argparse

#识别的关键词为 /*AUTO_DFF [WD]dff_inst=3'd2 moon_dffre_crc END*/
#最简单模式为 /*AUTO_DFF dff_inst END*/，表示1比特复位值为0的寄存器
#如果在文档中能够找到/*AUTO_DFF INSTWIRE*/则将所有的信号声明放在此处
#如果在文档中能够找到/*AUTO_DFF INSTREG*/则把所有的寄存器例化或always块放至于此处
#/*AUTO_DFF INSTREG*/必须在/*AUTO_DFF INSTWIRE*/后面，否则编译会有问题
#如果只有/*AUTO_DFF INSTREG*/，那么信号声明也都放在这里
#如果找不到对应的，则放在/*AUTO_DFF ... END*/下面

class DFF:#{{{
  exinfo_power  = True    #为False时不响应任何的额外信息
  wd_type       = "WD"    #例化时寄存器位宽的参数形式
  va_type       = "VA"    #例化时是否传如初始值
  clk_type      = "clk"   #时钟信号
  rst_type      = "rst_n" #复位信号
  dffre_rst_sel = False   #dffre/dffr是否需要传参VA项
  inst_type     = False   #True为例化形式

  def __init__(self, name, width="1", rst="", type="dffre", inst=inst_type):
    self.name = name      #寄存器例化名
    self.width = width    #寄存器为宽
    self.rst = rst        #寄存器复位值
    self.org_type = type  #寄存器原始类型, moon_dff dffr_pipe dffre_crc dffe dffre dffse dffve dffs dffv
    self.inst = inst      #是例化寄存器还是直接写always块
    
    #判断寄存器类型
    self.rst_en = True #是否有rst
    self.en_en  = True #是否有使能信号
    self.va_en  = True #是否传入复位值
    self.type = "dffre"
    if re.search("dffre", self.org_type):   #复位+使能，根据dffre_rst_sel来判断是否需要传参VA
      self.type = "dffre"
      self.va_en = DFF.dffre_rst_sel
    elif re.search("dffse", self.org_type): #置位+使能，复位值为全1
      self.type = "dffse"
      self.va_en = False
    elif re.search("dffve", self.org_type): #置位+使能，复位值为指定，否则为全0
      self.type = "dffve"
    elif re.search("dffr", self.org_type):  #复位，复位值为全0
      self.type = "dffr"
      self.en_en = False
      self.va_en = DFF.dffre_rst_sel
    elif re.search("dffs", self.org_type):  #置位，复位值为全1
      self.type = "dffs"
      self.en_en = False
      self.va_en = False
    elif re.search("dffv", self.org_type):  #置位，复位值为指定，否则为全0
      self.type = "dffv"
      self.en_en  = False
    elif re.search("dffe", self.org_type):  #使能，无复位值
      self.type = "dffe"
      self.rst_en = False
      self.va_en  = False
    elif re.search("dff", self.org_type):   #纯打拍寄存器，无复位无使能
      self.type = "dff"
      self.en_en  = False
      self.rst_en = False
      self.va_en  = False

    #判断附加信息
    res_exinfo  = re.search("dff\w+_(\w+)$", self.org_type)
    self.exinfo = ""
    if res_exinfo:
      self.exinfo = res_exinfo.group(1)
      if re.search("pipe", self.exinfo):
        self.exinfo = ""
    
    self.exinfo_power = False
    if DFF.exinfo_power == True and self.exinfo != "":
      self.exinfo_power = True

    #判断寄存器复位值
    if self.rst == "":
      if self.type == "dffre" or self.type == "dffr" or self.type == "dffve" or self.type == "dffv":
        if re.search("\W+", self.width):
          self.rst = "{{({0}){{1'b0}}}}".format(self.width)
        else:
          self.rst = "{{{0}{{1'b0}}}}".format(self.width)
      else:
        if re.search("\W+", self.width):
          self.rst = "{{({0}){{1'b1}}}}".format(self.width)
        else:
          self.rst = "{{{0}{{1'b1}}}}".format(self.width)

    pass

  def gen_sig_declare(self): #声明信号{{{
    sig_d  = "wire [{0} -1:0]{1}_d;\n".format(self.width, self.name)
    sig_q  = "wire [{0} -1:0]{1}_q;\n".format(self.width, self.name)
    
    sig_en = ""
    if self.en_en:
      len_width = len(self.width) + 7
      sig_en = "wire {0}{1}_en;\n".format(" "*len_width, self.name)
    sig_declare = sig_d + sig_q + sig_en
    
    #如果有附加信息，那么声明一个附加信号在这
    if self.exinfo_power == True:
      sig_exinfo   = "wire {0}{1}_{2};\n".format(" "*len_width, self.name, self.exinfo)
      sig_declare += sig_exinfo
    return sig_declare
    #}}}

  def gen_reg_inst(self): #采用例化寄存器的方式{{{
    ret = "wire [{0} -1:0]{1} = {1}_q;\n".format(self.width, self.name) #先声明下寄存器本身
    
    if self.va_en:
      ret += f"{self.org_type} #(.{DFF.wd_type}({self.width}), .{DFF.va_type}({self.rst}))\n"
    else:
      ret += f"{self.org_type} #(.{DFF.wd_type}({self.width}))\n"
    
    ret += f"u_{self.name}_dff (\n"
    ret += f"  .clk   ({DFF.clk_type}),\n"
    
    if self.rst_en:
      ret += f"  .rst_n ({DFF.rst_type}),\n"
    
    ret += f"  .d     ({self.name}_d),\n"
    ret += f"  .q     ({self.name}_q)"

    if self.en_en:
      ret += f",\n"
      ret += f"  .en    ({self.name}_en)"
    
    if self.exinfo_power:
      ret += f",\n"
      ret += f"  .{self.name}_{self.exinfo} ({self.name}_{self.exinfo})"

    ret += ");"
    
    return ret
  #}}}

  def gen_reg_uninst(self): #采用手写寄存器的方式{{{
    ret  = "reg  [{0} -1:0]{1};\n".format(self.width, self.name) #先声明下寄存器, reg类型
    
    if self.rst_en: #带复位
      ret += f"always @(posedge {DFF.clk_type} or negedge {DFF.rst_type})begin\n"
      ret += f"  if(!{DFF.rst_type})\n"
      ret += f"    {self.name} <= {self.rst};\n"
      if self.en_en:
        ret += f"  else if({self.name}_en)\n"
        ret += f"    {self.name} <= {self.name}_d;\n"
      else:
        ret += f"  else\n"
        ret += f"    {self.name} <= {self.name}_d;\n"
    else:           #不带复位
      ret += f"always @(posedge {DFF.clk_type})begin\n"
      if self.en_en:
        ret += f"  if({self.name}_en)\n"
        ret += f"    {self.name} <= {self.name}_d;\n"
      else:
        ret += f"  {self.name} <= {self.name}_d;\n"
    ret += f"end\n"
    ret += "assign {0}_q = {0};".format(self.name)

    return ret
    #}}}

  def instwire_ragion_str(self): #有/*AUTO_DFF INSTWIRE*/时，对其的输出#{{{
    ret  = ""
    ret += self.gen_sig_declare()
    return ret

  def instreg_ragion_str(self): #有/*AUTO_DFF INSTREG*/时，对其的输出
    ret  = ""
    if self.inst:
      ret += self.gen_reg_inst()
    else:
      ret += self.gen_reg_uninst()
    return ret
  
  def only_instreg_ragion_str(self): #只有/*AUTO_DFF INSTREG*/时，对其的输出
    ret  = ""
    ret += self.instwire_ragion_str()
    ret += self.instreg_ragion_str()
    return ret
  #}}}

#}}}  

def input_args_proc():#{{{
  parser = argparse.ArgumentParser(description="argparse info")
  parser.add_argument('-o', action='store_true', default=False, help='open this script')
  parser.add_argument('-d', action='store_true', default=False, help='del auto generate')
  parser.add_argument('-f', help='input name')
  result = parser.parse_args()
  if result.o == True:
      os.system("gvim %s" % __file__)
      sys.exit(0)
  del_gen = 0
  if result.d == True:
      del_gen = 1
  if not result.f:
      raise ValueError("No input file")
  inp_file = result.f
  return inp_file, del_gen
pass#}}}

def del_gen_code(inp_file):#{{{
  tmp_list = []
  shot_en = 0
  with open(inp_file, "r") as hd:
    handle = hd.readlines()
    for line in handle:
      line = line.rstrip()
      if re.search(r"//AUTO_DFF_START", line):
        shot_en = 1
      if shot_en == 0:
        tmp_list.append(line)
      if re.search(r"//AUTO_DFF_END", line):
        shot_en = 0
  return tmp_list
#}}}

def sys_input_file(inp_file): #{{{
  global dff_dict
  global key_pattern

  dff_dict = {}
  handle = del_gen_code(inp_file)

  instwire = False
  instreg  = False
  for line in handle:
    name  = ""
    width = "1"
    rst   = ""
    type  = "dffre"

    res_inst_wire = re.search(r"\/\*AUTO_DFF INSTWIRE\*\/", line)
    res_inst_reg  = re.search(r"\/\*AUTO_DFF INSTREG\*\/", line)

    key_pattern = re.compile(r"\/\*\s*AUTO_DFF\s+(\[.*\])?\s*(\w+)\s*(\=\s*.*\s+)?(\w+)?\s+END\s*\*\/")
    #                                            1           2       3            4
    res = key_pattern.search(line)

    if instwire == False and res_inst_wire:
      instwire = True
    if instreg == False and res_inst_reg:
      instreg = True

    if res:
      name = res.group(2)
      if res.group(1): #[WD]
        tmp   = res.group(1)
        width = re.match(r"\[(.*)\]", tmp).group(1)
      if res.group(3): #=8'h4
        tmp = res.group(3).strip()
        rst = re.match(r"\=\s*(.*)", tmp).group(1)
      if res.group(4): #dffre
        type = res.group(4).strip()
      dff = DFF(name, width, rst, type)
      #print(name, width, rst, type)
      dff_dict[name] = dff

  return instwire, instreg
#}}}

def gen_output(inp_file, instwire, instreg): #{{{
  handle = del_gen_code(inp_file)
  output_list = []

  for line in handle:
    res_inst_wire = re.search(r"\/\*AUTO_DFF INSTWIRE\*\/", line)
    res_inst_reg  = re.search(r"\/\*AUTO_DFF INSTREG\*\/", line)
    res_dff       = key_pattern.search(line)
    if instwire and instreg: #声明和例化区域都有
      if res_inst_wire:
        output_list.append(line)
        output_list.append("//AUTO_DFF_START")
        for name in dff_dict.keys():
          dff = dff_dict[name]
          output_list.append(dff.instwire_ragion_str())
        output_list.append("//AUTO_DFF_END")
      elif res_inst_reg:
        output_list.append(line)
        output_list.append("//AUTO_DFF_START")
        for name in dff_dict.keys():
          dff = dff_dict[name]
          output_list.append(dff.instreg_ragion_str() + "\n")
        output_list.append("//AUTO_DFF_END")
      else:
        output_list.append(line)
    elif instwire: #只有声明区域，例化跟随寄存器声明走
      if res_inst_wire:
        output_list.append(line)
        output_list.append("//AUTO_DFF_START")
        for name in dff_dict.keys():
          dff = dff_dict[name]
          output_list.append(dff.instwire_ragion_str())
        output_list.append("//AUTO_DFF_END")
      elif res_dff:
        name = res_dff.group(2)
        dff  = dff_dict[name]
        output_list.append(line)
        output_list.append("//AUTO_DFF_START")
        output_list.append(dff.instreg_ragion_str() + "\n")
        output_list.append("//AUTO_DFF_END")
      else:
        output_list.append(line)
    elif instreg: #只有例化区域，所有的东西都放在例化区域
      if res_inst_reg:
        output_list.append(line)
        output_list.append("//AUTO_DFF_START")
        for name in dff_dict.keys():
          dff = dff_dict[name]
          output_list.append(dff.only_instreg_ragion_str() + "\n")
        output_list.append("//AUTO_DFF_END")
      else:
        output_list.append(line)
    else: #啥区域都没有
      #print("no INSTWIRE and INSTREG")
      if res_dff:
        name = res_dff.group(2)
        dff  = dff_dict[name]
        output_list.append(line)
        output_list.append("//AUTO_DFF_START")
        output_list.append(dff.only_instreg_ragion_str())
        output_list.append("//AUTO_DFF_END")
      else:
        output_list.append(line)

  return output_list
#}}}        

def gen_code(inp_file):
  instwire, instreg = sys_input_file(inp_file)
  output_list = gen_output(inp_file, instwire, instreg)
  return output_list

def main():
  return_list = []
  inp_file, del_gen = input_args_proc()
  
  if del_gen == 1:
    return_list = del_gen_code(inp_file)
  else:
    return_list = gen_code(inp_file)
  
  for line in return_list:
    print(line)
    pass

if __name__ == "__main__":
  main()
